package main

import (
	"encoding/json"
	"log"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

type Client struct {
	hub    *Hub
	conn   *websocket.Conn
	roomId string
	send   chan Result
	auth   bool
}

type Result struct {
	Code    int    `json:"code"`
	MsgType string `json:"msgType"`
	Data    any    `json:data`
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

const (
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second

	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second

	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10

	// Maximum message size allowed from peer.
	maxMessageSize = 512
)

func sendInfo(msg string, client *Client) {
	client.hub.rooms[client.roomId].broadcast <- Result{
		MsgType: "info",
		Data:    msg,
	}
}

// serve websocket connection
func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err.Error())
	}
	client := &Client{hub: hub, conn: conn, send: make(chan Result, 256)}

	//注册到hub中
	client.hub.register <- client

	conn.WriteJSON(Result{
		MsgType: "info",
		Data:    "连接ws成功",
	})
	//写入信息
	go client.writePump()
	//读取信息
	go client.readPump()
}

func (c *Client) readPump() {
	defer func() {
		c.hub.unregister <- c
		//从房间中移除
		if c.roomId != "" {
			delete(c.hub.rooms[c.roomId].clients, c)
		}
		c.conn.Close()
	}()
	c.conn.SetReadLimit(maxMessageSize)
	c.conn.SetReadDeadline(time.Now().Add(pongWait))
	c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, message, err := c.conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				log.Printf("error: %v", err)
			}
			break
		}

		//解析消息
		var result Result
		err = json.Unmarshal(message, &result)
		if err != nil {
			log.Println("解析消息失败" + err.Error())
			continue
		}

		log.Println("接收到消息", result)

		c.msgHandle(result)
	}
}

func (c *Client) msgHandle(message Result) {
	//1.判断消息的类型
	switch message.MsgType {
	case "register":
		//验证身份注册信息
		c.auth = true
		c.send <- Result{
			MsgType: "info",
			Data:    "注册成功",
		}
		break
	case "join":
		//加入房间信息
		c.roomId = message.Data.(string)
		//判断房间是否存在
		if _, ok := c.hub.rooms[c.roomId]; !ok {
			//不存在房间需要创建
			c.hub.rooms[c.roomId] = &HubRoom{
				RoomId:     c.roomId,
				clients:    make(map[*Client]bool),
				broadcast:  make(chan Result, 50),
				register:   make(chan *Client, 50),
				unregister: make(chan *Client, 50),
			}
			//启动房间协程
			go c.hub.rooms[c.roomId].run(func() {
				//回掉删除房间信息
				delete(c.hub.rooms, c.roomId)
			})
		}
		//加入房间
		c.hub.rooms[c.roomId].clients[c] = true

		sendInfo("加入房间成功", c)
		break
	case "leave":
		if c.roomId == "" {
			return
		}
		//离开房间信息
		delete(c.hub.rooms[c.roomId].clients, c)
		sendInfo("离开房间成功", c)
		c.roomId = ""
		break
	case "offer":
		//发送offer
	case "answer":
		//发送answer
	case "candidate":
		//发送candidate
		if c.roomId == "" {
			return
		}
		c.hub.rooms[c.roomId].broadcast <- message
		break
	}

}

func (c *Client) writePump() {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		c.conn.Close()
	}()
	for {
		select {
		case message, ok := <-c.send:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if !ok {
				// The hub closed the channel.
				c.conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}

			w, err := c.conn.NextWriter(websocket.TextMessage)
			if err != nil {
				return
			}
			data, _ := json.Marshal(message)
			w.Write(data)

			// Add queued chat messages to the current websocket message.
			n := len(c.send)
			for i := 0; i < n; i++ {
				w.Write([]byte{'\n'})
				data, _ = json.Marshal(<-c.send)
				w.Write(data)
			}

			if err := w.Close(); err != nil {
				return
			}
		case <-ticker.C:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				return
			}
		}
	}
}
